
use std::path::Path;

use clap::{Parser, ValueEnum};
use anyhow::{Context, Ok, Result};

#[derive(ValueEnum, Clone, Copy, Default)]
enum TargetType {
    #[default]
    #[clap(help = "自动选择")]
    Auto,
    #[clap(help = "保持原格式")]
    Keep,
    #[clap(help = "PNG 格式")]
    Png,
    #[clap(help = "JPEG 格式")]
    Jpeg,
}

#[derive(ValueEnum, Clone, Copy, Default)]
enum PngAlphaChannelMode {
    #[default]
    #[clap(help = "保持alpha通道")]
    Retain,
    #[clap(help = "移除alpha通道")]
    Remove,
    #[clap(help = "添加alpha通道")]
    Add,
    #[clap(help = "如果无alpha通道，转换为jpeg")]
    AutoConvertJpeg
}


#[derive(Parser)]
struct Cli {
    #[arg(help = "源图片路径")]
    source: String,
    #[arg(help = "目标图片路径")]
    target: String,
    #[arg(short, long, help = "目标图片类型", value_enum, default_value_t  = TargetType::Auto)]
    target_type: TargetType,
    #[arg(short, long, help = "PNG alpha通道处理方式", value_enum, default_value_t = PngAlphaChannelMode::Retain)]
    png_alpha_channel_mode: PngAlphaChannelMode,
}

fn main() {
    let cli = Cli::parse();
    convert_image(&cli.source, &cli.target, cli.target_type, cli.png_alpha_channel_mode).unwrap();
}

fn convert_image(source: &str, target: &str, target_type: TargetType, png_alpha_channel_mode: PngAlphaChannelMode) -> Result<()>{
    let ImageData{image, format} = load_image(source).context("加载图片失败")?;

    let out_image_data: ImageData = match target_type {
        TargetType::Auto => {
            //根据target的后缀名判断
            let format = image::ImageFormat::from_path(&target).unwrap_or(format);
            match format {
                image::ImageFormat::Png => convert_png(image, png_alpha_channel_mode)?,
                image::ImageFormat::Jpeg => convert_jpeg(image)?,
                _ => {
                    eprintln!("未知图片格式，自动保持原格式");
                    ImageData {
                        image: image,
                        format: format
                    }
                }
            }
        },
        TargetType::Keep => {
            match format {
                image::ImageFormat::Png => convert_png(image, png_alpha_channel_mode)?,
                image::ImageFormat::Jpeg => convert_jpeg(image)?,
                _ => {
                    eprintln!("未知图片格式，自动保持原格式");
                    ImageData {
                        image: image,
                        format: format
                    }
                }
            }
        },
        TargetType::Png => convert_png(image, png_alpha_channel_mode)?,
        TargetType::Jpeg => convert_jpeg(image)?,
    };

    save_image(&out_image_data, target).context("保存图片失败")?;

    Ok(())
}

fn convert_jpeg(image: image::DynamicImage) -> Result<ImageData> {
    let image = image::DynamicImage::ImageRgb8(image.to_rgb8());
    Ok(ImageData {
        image,
        format: image::ImageFormat::Jpeg
    })
}

fn convert_png(image: image::DynamicImage, png_alpha_channel_mode: PngAlphaChannelMode) -> Result<ImageData> {
    match png_alpha_channel_mode {
        PngAlphaChannelMode::Retain => Ok(ImageData {
            image,
            format: image::ImageFormat::Png
        }),
        PngAlphaChannelMode::Remove => {
            let image = image.to_rgb8();
            let image = image::DynamicImage::ImageRgb8(image);
            Ok(ImageData {
                image,
                format: image::ImageFormat::Png
            })
        },
        PngAlphaChannelMode::Add => {
            let image = image.to_rgba8();
            let image = image::DynamicImage::ImageRgba8(image);
            Ok(ImageData {
                image,
                format: image::ImageFormat::Png
            })
        },
        PngAlphaChannelMode::AutoConvertJpeg => {
            match image.color().has_alpha() {
                true => Ok(ImageData {
                    image,
                    format: image::ImageFormat::Png
                }),
                false => convert_jpeg(image)
            }
        }
    }
}

struct ImageData {
    pub image: image::DynamicImage,
    pub format: image::ImageFormat,
}

fn load_image<P: AsRef<Path>>(path: P) -> Result<ImageData> {
    // let image = image::open(&path)?;
    let format = image::ImageFormat::from_path(&path)?;
    let binary = std::fs::read(&path)?;
    let image = image::load_from_memory(&binary)?;
    Ok(ImageData { image, format })
}

fn save_image<P: AsRef<Path>>(image: &ImageData, path: P) -> Result<()> {
    image.image.save_with_format(path, image.format)?;
    Ok(())
}

